#pragma once

#include <stack>

namespace battle::motion
{

    class MovementGenerator;
    class MotionMaster : private std::stack<battle::motion::MovementGenerator*>
    {
    private:
        typedef std::stack<battle::motion::MovementGenerator*> Impl;
        typedef std::vector<battle::motion::MovementGenerator*> ExpireList;

    public:
        explicit MotionMaster(battle::object::Unit* unit) : m_owner(unit), m_expList(NULL), m_cleanFlag(MMCF_NONE) {}
        ~MotionMaster();

        void Initialize();

        battle::motion::MovementGenerator const* GetCurrent() const { return top(); }

        using Impl::top;
        using Impl::empty;

        typedef Impl::container_type::const_iterator const_iterator;
        const_iterator begin() const { return Impl::c.begin(); }
        const_iterator end() const { return Impl::c.end(); }

        void UpdateMotion(unsigned int diff);
        void Clear(bool reset = true, bool all = false)
        {
            if (m_cleanFlag & MMCF_UPDATE)
            {
                DelayedClean(reset, all);
            }
            else
            {
                DirectClean(reset, all);
            }
        }
        void MovementExpired(bool reset = true)
        {
            if (m_cleanFlag & MMCF_UPDATE)
            {
                DelayedExpire(reset);
            }
            else
            {
                DirectExpire(reset);
            }
        }

        void MoveIdle();
        void MoveRandomAroundPoint(float x, float y, float z, float radius, float verticalZ = 0.0f);
        void MoveTargetedHome();
        void MoveFollow(battle::object::Unit* target, float dist, float angle);
        void MoveChase(battle::object::Unit* target, float dist = 0.0f, float angle = 0.0f);
        void MoveConfused();
        void MoveFleeing(battle::object::Unit* enemy, unsigned int timeLimit = 0);
        void MovePoint(unsigned int id, float x, float y, float z, bool generatePath = true);
        void MoveSeekAssistance(float x, float y, float z);
        void MoveSeekAssistanceDistract(unsigned int timer);
        void MoveWaypoint(int id = 0, unsigned int source = 0, unsigned int initialDelay = 0, unsigned int overwriteEntry = 0);
        void MoveTaxiFlight(unsigned int path, unsigned int pathnode);
        void MoveDistract(unsigned int timeLimit);
        void MoveFall();
        void MoveFlyOrLand(unsigned int id, float x, float y, float z, bool liftOff);

        MovementGeneratorType GetCurrentMovementGeneratorType() const;

        void PropagateSpeedChange();
        bool SetNextWaypoint(unsigned int pointId);

        unsigned int getLastReachedWaypoint() const;
        void GetWaypointPathInformation(std::ostringstream& oss) const;
        bool GetDestination(float& x, float& y, float& z);

    private:
        void Mutate(battle::motion::MovementGenerator* m);                  // use Move* functions instead

        void DirectClean(bool reset, bool all);
        void DelayedClean(bool reset, bool all);

        void DirectExpire(bool reset);
        void DelayedExpire(bool reset);

        battle::object::Unit* m_owner;
        ExpireList* m_expList;
        unsigned char       m_cleanFlag;
    };

}